import numpy as np
import cv2
from matplotlib import pyplot as plt


def conv(image, kernel, mode='same') -> np.array:

    if mode == 'fill':  #选择是否进行边缘填充
        h = kernel.shape[0] // 2   #卷积核的列整除2
        w = kernel.shape[1] // 2   #卷积核的行整除2
        #在原始图像边缘进行填充，常数填充，填数值0
        image = np.pad(image, ((h, h), (w, w), (0, 0)), 'constant')

    #进行卷积运算
    conv_b = convolve(image[:, :, 0], kernel)
    conv_g = convolve(image[:, :, 1], kernel)
    conv_r = convolve(image[:, :, 2], kernel)
    res = np.dstack([conv_b, conv_g, conv_r])
    return res


def convolve(image, kernel) ->np.array:
    h_kernel, w_kernel = kernel.shape  #获取卷积核的长宽，也就是行数和列数

    h_image, w_image = image.shape   #获取欲处理图片的长宽

    #计算卷积核中心点开始运动的点
    res_h = h_image - h_kernel + 1
    res_w = w_image - w_kernel + 1

    #生成一个numpy数组，用于保存处理后的图片
    res = np.zeros((res_h, res_w), np.uint8)

    for i in range(res_h):
        for j in range(res_w):
            #image处传入的是一个与卷积核一样大小矩阵，这个矩阵取自于欲处理图片的一部分
            #这个矩阵与卷核进行运算，用i与j来进行卷积核滑动
            res[i, j] = getMultiplyAns(image[i:i + h_kernel, j:j + w_kernel], kernel)

    return res

#两个数组(矩阵)，点对点相乘后进行累加
def getMultiplyAns(image, kernel) ->np.array:
    res = np.multiply(image, kernel).sum()
    if res > 255:
        return 255
    elif res<0:
        return 0
    else:
        return res

#高斯滤波去除噪声点
def Gauss(image) ->np.array:
    #生成5*5的高斯核
    gaussKernel=np.array([[1/273,4/273,7/273,4/273,1/273],
                          [4/273,16/273,26/273,16/273,4/273],
                          [7/273,26/273,41/273,26/273,7/243],
                          [4/273,16/273,26/273,16/273,4/273],
                          [1/273,4/273,7/273,4/273,1/273]])
    ans=image.copy()
    ans=conv(ans,gaussKernel,'fill')
    return ans


#均值滤波去除噪声点
def mean(image)-> np.array:
    meanKernel=np.array([[1/25,1/25,1/25,1/25,1/25],
                         [1/25,1/25,1/25,1/25,1/25],
                         [1/25,1/25,1/25,1/25,1/25],
                         [1/25,1/25,1/25,1/25,1/25],
                         [1/25,1/25,1/25,1/25,1/25]])
    ans=image.copy()
    ans=conv(ans,meanKernel,'fill')
    return ans


#中值滤波去除噪声点,size为指定的领域大小
def median(image,size) -> np.array:
    image=cv2.cvtColor(image,cv2.COLOR_RGB2GRAY)
    h=image.shape[0]
    w=image.shape[1]
    ans=image.copy()
    row=h-size+1
    col=w-size+1
    vv=np.zeros((size,size),dtype=int)
    for i in range(row):
        for j in range(col):
            for u in range(i,i+size):
                for v in range(j,j+size):
                    vv[u-i,v-j]=image[u,v]
            kkk=vv.ravel()
            np.sort(kkk)
            ans[i+size//2,j+size//2]=kkk[(len(kkk)+1)//2]
            vv.fill(0)
    return ans

#图像分割算法
def image_split(img):
    gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    ishow=img.copy()

    #对图像进行二值化处理，以便距离变换函数操作
    ret,thresh=cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)

    #生成一个核，用于进行开运算
    kernel=np.ones((3,3),np.uint8)

    #进行开运算，并且设置迭代次数为2次，以去除噪声
    opening=cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel,iterations=2)

    #再进行膨胀操作
    sure_bg=cv2.dilate(opening,kernel,iterations=3)

    #进行距离变换,得到图像“中心”
    dist_transform=cv2.distanceTransform(opening,cv2.DIST_L2,5)

    #再次二值化
    ret,sure_fg=cv2.threshold(dist_transform,0.7*dist_transform.max(),255,0)
    sure_fg=np.uint8(sure_fg)

    #通过减法运算得出未知区域
    unknown=cv2.subtract(sure_bg,sure_fg)

    #进行标注，大致勾画出期盼分割区域
    ret,markers=cv2.connectedComponents(sure_fg)
    markers=markers+1
    markers[unknown==255]=0

    #使用分水岭算法
    markers=cv2.watershed(img,markers)
    img[markers==-1]=[0,255,0]
    cv2.imshow("original iamge",ishow)
    cv2.imshow("split image",img)


if __name__ == '__main__':
    image = cv2.imread("lena_noise.bmp",flags=cv2.COLOR_RGB2GRAY)
    cv2.imshow("original image",image)

    ans0=Gauss(image)
    cv2.imshow("Gauss blur",ans0)

    ans1=mean(image)
    cv2.imshow("Mean blur",ans1)

    ans2=median(image,5)
    cv2.imshow("median blur",ans2)

    #老师给的图像名是中文的，在读取图像时会出现问题，所以这里修改了文件名为英文的
    img=cv2.imread("announcer .jpg")
    image_split(img)

    cv2.waitKey(-1)
